#include "Game_Logic.h"

Game_Logic::Game_Logic(Game_view* gv)
{
    mp_game_table = new grid();//Initialize playing table
    gamestate = GS_INITIALIZING;
    gameV = gv;

    for(int i=0; i<3;i++)
        timers[i] = 0;

    current_piece= NULL;
    next_piece=NULL;
}

Game_Logic::~Game_Logic()
{
    SAFE_DELETE(mp_game_table);
    SAFE_DELETE(current_piece);
}

//Callback function used to move the pieces
Uint32 Move_tiemout(Uint32 time, void* gameL_obj)
{
    ((class Game_Logic *)gameL_obj)->UpdateGrid(2);
    return time;
}

bool Game_Logic::Update()
{

    switch(gamestate)
    {
        case GS_INITIALIZING:
        {

            //Take new piece
            current_piece = Random_piece();
            next_piece = Random_piece();

            gameV->mp_game_table = mp_game_table;
            gameV->current_piece = current_piece;
            gameV->next_piece = next_piece;

            //Initialize falling timer
            piece_down_timer = SDL_AddTimer(1000, Move_tiemout, this);

            gamestate = GS_RUNNING;

            break;
        }

        case GS_RUNNING:
        {
            if(!HandleEvents())
                return false;

            break;
        }

        case GS_GAMEOVER:
        {
            if(!HandleEvents())
                return false;

            break;
        }

        //State used when we have to wait for the Game View
        case GS_WAIT:
        {

        }

        //default case
        default:
        {
            std::string error = "Game Logic: Strange gamestate. Something weird happened...";
            throw error;
        break;
        }
    }

    return true;
}

//returns a pointer to a new piece randomly generated
piece* Game_Logic::Random_piece()
{
    int row = GRID_HEIGHT-1;
    int column = GRID_WIDTH/2;
    int r_piece;
    int highest=7;

    r_piece = (rand()%highest)+1;

    switch(r_piece)
    {
        case 1:
            return new t_block(row, column);
        break;

        case 2:
            return new rl_block(row, column);
        break;

        case 3:
            return new l_block(row,column);
        break;

        case 4:
            return new line_block(row, column);
        break;

        case 5:
            return new square_block(row, column);
        break;

        case 6:
            return new s_block(row, column);
        break;

        case 7:
            return new z_block(row, column);
        break;

        default:
            return new t_block(row, column);
        break;
    }
}

//Handles events like input from keyboard, etc...
bool Game_Logic::HandleEvents()
{
    SDL_Event event;
    unsigned int m_Keys[SDLK_LAST];

    Uint32 time = SDL_GetTicks();
    SDLKey sym;

    if(gamestate == GS_RUNNING)
    {
        //check keys timers
        for(int i=0;i<3;i++)
        {
            if(timers[i]>0 && (time-timers[i]>=100))
            {
                UpdateGrid(i);
                timers[i]= SDL_GetTicks();
            }

        }
    }

    while (SDL_PollEvent(&event))//get all events
    {
        if(gamestate == GS_RUNNING)
        {
            switch (event.type)
            {
                case SDL_KEYDOWN:
                {
                    sym = event.key.keysym.sym;

                    if(sym == SDLK_ESCAPE) //Quit if escape was pressed
                    {
                        return false;
                    }

                    if(sym == SDLK_SPACE) //Rotate piece
                    {
                        current_piece->m_Rotate();
                        if(!Check_new_position(0,0))
                        {
                            current_piece->m_Rotate();
                            current_piece->m_Rotate();//OK, it's dumb... but it works
                            current_piece->m_Rotate();
                        }

                    }

                    if(sym == SDLK_LEFT) //Move Left
                    {
                        UpdateGrid(LEFT);
                        timers[LEFT] = SDL_GetTicks();
                    }

                    if(sym == SDLK_RIGHT) //Move Right
                    {
                        UpdateGrid(RIGHT);
                        timers[RIGHT] = SDL_GetTicks();
                    }

                    if(sym == SDLK_DOWN) //Move Down
                    {
                        UpdateGrid(DOWN);
                        timers[DOWN] = SDL_GetTicks();
                    }

                    m_Keys[sym] = 1;
                    break;
                }

                case SDL_KEYUP:
                {

                    SDLKey sym = event.key.keysym.sym;

                    if(sym == SDLK_LEFT) //Move Left
                    {
                        timers[LEFT] = 0;
                    }

                    if(sym == SDLK_RIGHT) //Move Right
                    {
                        timers[RIGHT] = 0;
                    }

                    if(sym == SDLK_DOWN) //Move Down
                    {
                        timers[DOWN] = 0;
                    }

                    m_Keys[sym] = 0;
                    break;
                }

                case SDL_VIDEORESIZE:
                {
                    //the window has been resized so we need to set up our viewport and projection according to the new size
                    gameV->resize(event.resize.w, event.resize.h);
                    break;
                }

                case SDL_USEREVENT:
                {
                    //Handle events coming from game logic
                    switch(event.user.code)
                    {

                    }
                    break;
                }

                // Default case
                default:
                {
                    break;
                }
            }
        }

        //If we are in game_over
        if(gamestate == GS_GAMEOVER)
        {
            switch(event.type)
            {
                case SDL_KEYDOWN:
                {
                    sym = event.key.keysym.sym;

                    if(sym == SDLK_ESCAPE) //Quit if escape was pressed
                    {
                        return false;
                    }

                    m_Keys[sym] = 1;
                    break;
                }

                case SDL_VIDEORESIZE:
                {
                    //the window has been resized so we need to set up our viewport and projection according to the new size
                    gameV->resize(event.resize.w, event.resize.h);
                    break;
                }
            }
        }

    }

	return true;
}

bool Game_Logic::Check_new_position(int mov_x, int mov_y)
{
    int size = current_piece->getSize();
    int position[2] = {current_piece->getPosition(0), current_piece->getPosition(1)};

    for(int i=0; i<size; i++)
    {
       for(int j=0; j<size; j++)
       {
           if(current_piece->getMatPos(i,j))
           {
               //If we are outisde the game board, return false
               if(  ((GRID_WIDTH-1)<(position[1]+j+mov_x)) || (0>(position[1]+j+mov_x)) || (0>(position[0]-i+mov_y)) )
               {
                   return false;
               }
               //If we collide with another block, return false
               else
               {
                   if((mp_game_table->Get_board_pos(position[0]-i+mov_y, position[1]+j+mov_x))->full)
                        return false;
               }
           }
       }
    }

    return true;
}

//Used to copy the piece to the grid
void Game_Logic::Copy_piece()
{
    int size = current_piece->getSize();
    int position[2] = {current_piece->getPosition(0), current_piece->getPosition(1)};
    bool heightfound = false;

    for(int i=0; i<size; i++)
    {
       for(int j=0; j<size; j++)
       {
           if(current_piece->getMatPos(i,j))
           {
                mp_game_table->Set_board_pos(position[0]-i,position[1]+j,true, current_piece->getColor());
                if(!heightfound)
                {
                    heightfound = true;
                    if(position[0]-i > mp_game_table->pile_height)
                        mp_game_table->pile_height = position[0]-i;
                }
           }
       }
    }


}

void Game_Logic::UpdateGrid(int update_type)
{

    if(current_piece != NULL)
    {
        switch(update_type)
        {
            case LEFT:
            {
                if(Check_new_position(-1,0))
                    current_piece->Update_position(current_piece->getPosition(0),current_piece->getPosition(1)-1);

                break;
            }

            case RIGHT:
            {
                if(Check_new_position(1,0))
                    current_piece->Update_position(current_piece->getPosition(0),current_piece->getPosition(1)+1);

                break;
            }

            case DOWN:
            {
                if(Check_new_position(0,-1))
                {
                    current_piece->Update_position(current_piece->getPosition(0)-1,current_piece->getPosition(1));
                }
                else
                {

                    //Activate sound

                    //Piece is placed in the table
                    Copy_piece();

                    //clean lines of the table and update number of lines cleared
                    gameV->n_lines = gameV->n_lines + mp_game_table->clean_lines();

                    //delete current piece, take next piece and take a new next piece
                    delete current_piece;
                    current_piece = next_piece;
                    gameV->current_piece = current_piece;
                    next_piece = Random_piece();
                    gameV->next_piece = next_piece;

                    if(!Check_new_position(0,0))
                    {
                        SDL_RemoveTimer(piece_down_timer);
                        gamestate = GS_GAMEOVER;
                        gameV->setGameState(GS_GAMEOVER);
                    }

                }


                break;
            }
        }
    }

}
